<?php

use think\captcha\Captcha;
use think\Db;

// 异常错误报错级别,
error_reporting(E_ERROR | E_PARSE);

function C($name)
{
    $config = cache('systemConfig');
    if ($config) {
        $value = '';
        foreach ($config as $v) {
            if ($v['name'] == $name) {
                $value = $v['output_value'];
                break;
            }
        }

        return $value;
    } else {
        return Db::name('config')->where(['name' => $name])->order('sort asc')->value('value');
    }
}

/**
 * 系统加密方法
 * @param string $data   要加密的字符串
 * @param int    $expire 过期时间 (单位:秒)
 * @return string
 */
function thinkEncrypt($data, $expire = 0)
{
    $key = md5('083d9a270e6e16b2fbb08d35067aae5f');
    $data = base64_encode($data);
    $x = 0;
    $len = strlen($data);
    $l = strlen($key);
    $char = '';
    for ($i = 0; $i < $len; $i++) {
        if ($x == $l) $x = 0;
        $char .= substr($key, $x, 1);
        $x++;
    }
    $str = sprintf('%010d', $expire ? $expire + time() : 0);
    for ($i = 0; $i < $len; $i++) {
        $str .= chr(ord(substr($data, $i, 1)) + (ord(substr($char, $i, 1))) % 256);
    }

    return str_replace('=', '', base64_encode($str));
}

/**
 * 系统解密方法
 * @param string $data 要解密的字符串 （必须是think_encrypt方法加密的字符串）
 * @return string
 */
function thinkDecrypt($data)
{
    $key = md5('083d9a270e6e16b2fbb08d35067aae5f');
    $x = 0;
    $data = base64_decode($data);
    $expire = substr($data, 0, 10);
    $data = substr($data, 10);
    if ($expire > 0 && $expire < time()) {
        return '';
    }
    $len = strlen($data);
    $l = strlen($key);
    $char = $str = '';
    for ($i = 0; $i < $len; $i++) {
        if ($x == $l) $x = 0;
        $char .= substr($key, $x, 1);
        $x++;
    }
    for ($i = 0; $i < $len; $i++) {
        if (ord(substr($data, $i, 1)) < ord(substr($char, $i, 1))) {
            $str .= chr((ord(substr($data, $i, 1)) + 256) - ord(substr($char, $i, 1)));
        } else {
            $str .= chr(ord(substr($data, $i, 1)) - ord(substr($char, $i, 1)));
        }
    }

    return base64_decode($str);
}

/**
 * 检查验证码
 * */
function checkVerify($code)
{
    $verify = new Captcha();

    return $verify->check($code);
}

/**
 * 判断是否SSL协议
 * @return boolean
 */
function is_ssl()
{
    if (isset($_SERVER['HTTPS']) && ('1' == $_SERVER['HTTPS'] || 'on' == strtolower($_SERVER['HTTPS']))) {
        return true;
    } else if (isset($_SERVER['SERVER_PORT']) && ('443' == $_SERVER['SERVER_PORT'])) {
        return true;
    }

    return false;
}

/**
 * 获取域名前缀
 * */
function getHost()
{
    $res = is_ssl();
    if ($res) {
        return 'https://' . $_SERVER['HTTP_HOST'];
    } else {
        return 'http://' . $_SERVER['HTTP_HOST'];
    }
}

/**
 * 按综合方式输出通信数据
 * @param integer $result 状态码
 * @param string  $msg    提示信息
 * @param array   $data   数据
 * @return string
 */
function apiReturn($result, $msg = '', $data = false)
{
    if (!is_numeric($result)) {
        return '';
    }

    return apiJson($result, $msg, $data);
}

/**
 * 按json方式输出通信数据
 * @param integer $result 状态码
 * @param string  $msg    提示信息
 * @param array   $data   数据
 * @return string
 */
function apiJson($result, $msg = '', $data = false)
{

    if (!is_numeric($result)) {
        return '';
    }

    if ($data > 0 || $data === []) {
        $message = [
            'result' => $result,
            'msg' => $msg,
            'data' => $data ? : []
        ];
    } else {
        $message = [
            'result' => $result,
            'msg' => $msg,
            'data' => $data ? : ''
        ];
    }

    echo json_encode($message, JSON_UNESCAPED_UNICODE);
    exit;
}

/**
 * 根据图片id获取图片路径
 * */
function getPicture($id)
{
    if (preg_replace('/\D/s', '', $id) == $id) {
        //ID
        $info = Db::name('picture')->field('path')->where(['id' => $id])->find();
        if ($info) {
            return getHost() . $info['path'];
        } else {
            return '';
        }
    } else {
        //路径
        return $id;
    }
}

/**
 * 用户头像处理
 * @param int $avatar_id 用户头像ID 或 用户头像路径
 * @return string
 * */
function doAvatar($avatar_id)
{
    if (strlen($avatar_id) < 10) {
        //此时用户头像为修改之后的头像
        $avatar = getPicture($avatar_id);
    } else {
        $avatar = $avatar_id;
    }

    return $avatar ? : '';
}

/**
 * 多图处理
 * @param string $imgs    图片id集合
 * @param string $img_key 自定义图片路径下标
 * @return array
 * */
function doImgs($imgs, $img_key = 'path')
{
    $new_img_arr = [];
    if ($imgs) {
        $img_arr = explode(',', $imgs);

        if ($img_arr) {
            foreach ($img_arr as $k => $v) {
                $new_img_arr[$k]['id'] = $v;
                $new_img_arr[$k][$img_key] = getPicture($v);
            }
        }
    }

    return $new_img_arr;
}

/**
 * 获取[规格-属性]数据
 * @param int $goods_id 商品id
 * @return array
 * */
function getSku($goods_id)
{
    if (!cache('sku_' . $goods_id)) {
        $sku_value_list = Db::name('sku_value')->where(['goods_id' => $goods_id])->select();

        //$skus_id_arr：所有属性的id集合
        $skus_id_arr = [];
        $sku_list = [];
        if ($sku_value_list) {
            foreach ($sku_value_list as &$val) {
                $val['img'] = getPicture($val['img']);

                $sku_id = explode('-', $val['sku_id']);
                if ($sku_id) {
                    foreach ($sku_id as $value) {
                        if (!in_array($value, $skus_id_arr)) {
                            $skus_id_arr[] = $value;
                        }
                    }
                }
            }

            $parent_list = Db::name('sku')->distinct(true)->field('pid')->where(['id' => ['IN', $skus_id_arr]])->select();
            $parent_ids = array_column((array)$parent_list, 'pid');
            if ($parent_ids) {
                $sku_list = Db::name('sku')->field('id,name title')->where(['id' => ['IN', $parent_ids]])->select();
                if ($sku_list) {
                    foreach ($sku_list as &$v) {
                        $child_list = Db::name('sku')->field('id,name title')->where(['pid' => $v['id']])->select();
                        if ($child_list) {
                            foreach ($child_list as &$val) {
                                $val['class'] = '';
                                $val['status'] = false;
                                foreach ($sku_value_list as $value) {
                                    if (in_array($val['id'], explode('-', $value['sku_id'])) && $value['status']) {
                                        $val['status'] = true;
                                        break;
                                    }
                                }
                            }
                        } else {
                            $child_list = [];
                        }
                        $v['child'] = $child_list;
                    }
                } else {
                    $sku_list = [];
                }
            }
        } else {
            $sku_value_list = [];
        }

        $data = [
            'sku_list' => $sku_list,
            'sku_value_list' => $sku_value_list
        ];
        cache('sku_' . $goods_id, $data);
    } else {
        $data = cache('sku_' . $goods_id);
    }

    return $data;
}

/**
 * 微信违规文字检测
 * */
function checkContent($content)
{
    if ($access_token = accessToken()) {
        $checkUrl = 'https://api.weixin.qq.com/wxa/msg_sec_check?access_token=' . $access_token;
        $arr = [
            'content' => $content
        ];

        $result = httpRequest($checkUrl, $arr, 'json');
        $result = json_decode($result, true);

        if ($result['errcode'] != 0) apiReturn(2, '检测到文字含违规内容，请重新编辑');
    } else {
        apiReturn(2, 'access_token获取失败');
    }
}

function accessToken()
{
    $appid = C('XCX_APPID');
    $secret = C('XCX_SECRET');
    $tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $appid . "&secret=" . $secret;
    $tokenArr = file_get_contents($tokenUrl);
    $tokenArr = json_decode($tokenArr, true);

    return $tokenArr['access_token'];
}

function httpRequest($url, $data, $type)
{
    if ($type == 'json') {
        $headers = ["Content-type: application/json;charset=UTF-8", "Accept: application/json", "Cache-Control: no-cache", "Pragma: no-cache"];
        $data = json_encode($data, JSON_UNESCAPED_UNICODE);
    } else {
        $headers = [];
    }
    $curl = getCurl($url, $data);
    curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
    $output = curl_exec($curl);
    curl_close($curl);

    return $output;
}

/**
 * @param $url
 * @param $data
 * @return false|resource
 */
function getCurl($url, $data = null, $header = null)
{
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
    if (!empty($data)) {
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
    }
    // 设置header头
    if (!empty($header)) curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    return $curl;
}

/**
 * 一维数组去重
 * */
function a_array_unique($array)
{
    $out = [];
    foreach ($array as $key => $value) {
        if (!in_array($value, $out)) {
            $out[$key] = $value;
        }
    }

    return $out;
}

/**
 * 处理富文本信息
 * @param string $content 富文本内容
 * @return  string
 * */
function doEditorContent($content)
{
    return $content ? str_replace('<img src="', ' < img style = "width: 100%; height: auto" src = "' . getHost(), $content) : '';
}

/**
 * 格式化时间
 * @param int  $time 时间戳
 * @param date $date 时间格式
 * */
function timeFormat($time, $date = 'Y-m-d H:i:s')
{
    if ($time) {
        return date($date, $time);
    } else {
        return '';
    }
}

/**
 * @param array $sku_id 属性id集合
 * @return string
 * */
function getSkuName($sku_id)
{
    if ($sku_id) {
        $in_sku = implode(',', $sku_id);
        $order = ' field (id, ' . $in_sku . ' ) ';
        $sql = 'SELECT `name` FROM `tp_sku` WHERE `id` IN(' . $in_sku . ') ORDER BY ' . $order;
        return Db::query($sql);
    } else {
        return '';
    }
}

/**
 * 获取省id
 * @param string $province_name 省名称
 * @return int
 * */
function getProvinceId($province_name)
{
    return Db::name('province')->where(['name' => $province_name])->value('province_id');
}

/**
 * 获取省名称
 * @param int $province_id 省id
 * @return string
 * */
function getProvinceName($province_id)
{
    $name = Db::name('province')->where(['province_id' => $province_id])->value('name');

    return $name ? : '';
}

/**
 * 获取市id
 * @param string $province_name 省名称
 * @param string $city_name     市名称
 * @return int
 * */
function getCityId($province_name, $city_name)
{
    $province_id = getProvinceId($province_name);

    $city_name = str_replace(['市', '省'], '', $city_name);
    return Db::name('city')->where(['province_id' => $province_id, 'name' => ['like', $city_name . '%']])->value('city_id');
}

/**
 * 获取市名称
 * @param int $city_id 市id
 * @return string
 * */
function getCityName($city_id)
{
    $name = Db::name('city')->where(['city_id' => $city_id])->value('name');

    return $name ? : '';
}

/**
 * 获取区id
 * @param string $province_name 省名称
 * @param string $city_name     市名称
 * @param string $area_name     市名称
 * @return int
 * */
function getAreaId($province_name, $city_name, $area_name)
{
    $city_id = getCityId($province_name, $city_name);

    $area_name = str_replace(['市', '区', '县'], '', $area_name);
    return Db::name('area')->where(['city_id' => $city_id, 'name' => ['like', $area_name . '%']])->value('area_id');
}

/**
 * 获取区名称
 * @param int $area_id 区id
 * @return string
 * */
function getAreaName($area_id)
{
    $name = Db::name('area')->where(['area_id' => $area_id])->value('name');

    return $name ? : '';
}

function httpsRequest($url, $data = null, $header = null)
{
    $curl = getCurl($url, $data, $header);
    $output = curl_exec($curl);
    curl_close($curl);

    return $output;
}

/**
 * 获取access_token
 * @param string $token_file_name token存储文件名
 * @param int    $type            1:公众号token，2：小程序token
 * @return string
 *  */
function get_access_token($token_file_name = 'access_token.json', $type = 1, &$count = 0)
{
    if ($type == 1) {
        $config = [
            'appid' => C('GZH_APPID'),
            'secret' => C('GZH_SECRET'),
        ];
    } else {
        $config = [
            'appid' => C('XCX_APPID'),
            'secret' => C('XCX_SECRET'),
        ];
    }

    // access_token 应该全局存储与更新，以下代码以写入到文件中做示例
    $data = json_decode(get_php_file($token_file_name));
    if ($data->expires_time < time()) {
        // 如果是企业号用以下URL获取access_token
        $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $config['appid'] . "&secret=" . $config['secret'];
        $access_token_json = httpsRequest($url);
        $access_token = json_decode($access_token_json, true);
        $code = $access_token['errcode'] ? : 0;
        if ($code == 0) {
            $data->expires_time = time() + 3000;
            $data->access_token = $access_token;
            set_php_file($token_file_name, json_encode($data));

            $access_token = $access_token['access_token'];
        } else {
            if ($count < 3) {
                ++$count;
                get_access_token($token_file_name, $type, $count);
            } else {
                unlink(RUNTIME_PATH . '/' . $token_file_name);
                apiReturn(2, $access_token['errmsg']);
            }
        }
    } else {
        $access_token = $data->access_token->access_token;
    }

    return $access_token;
}

function get_php_file($filename)
{
    if (!is_file(RUNTIME_PATH . '/' . $filename)) {
        $data['expires_time'] = 0;
        $data['jsapi_ticket'] = '';
        set_php_file($filename, json_encode($data));
    }

    return file_get_contents(RUNTIME_PATH . '/' . $filename);
}

function set_php_file($filename, $content)
{
    file_put_contents(RUNTIME_PATH . '/' . $filename, $content);
}

/**
 * 注册用户 及 绑定用户
 * @param array $user_data 当前用户信息
 * @return array
 * */
function bindUser($user_data)
{
    $time = time();

    $user_info = Db::name('user')->where(['openid' => $user_data['openid']])->find();
    if ($user_info) {
        // 直接返回用户id和用户手机号
        $return_data = [
            'user_id' => $user_info['id'],
            'mobile' => $user_info['mobile'] ? : ''
        ];
    } else {
        // 新用户

        //注册用户信息 及 绑定
        $user_data['create_time'] = $time;

        $return_user_id = Db::name('user')->insertGetId($user_data);

        $return_data = [
            'user_id' => $return_user_id,
            'mobile' => ''
        ];
    }

    return $return_data;
}

/**
 * 生成用户分享码（公众号二维码[带用户id]）
 * @param string $user_id 用户id
 * @return string
 * */
function createUserQr($user_id)
{
    $qr = Db::name('user')->where(['id' => $user_id])->value('qr');
    if ($qr == 0) {
        $access_token = get_access_token();

        $data = '{"action_name": "QR_LIMIT_STR_SCENE", "action_info": {"scene": {"scene_str": "' . $user_id . '"}}}';

        $url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" . $access_token;

        $res = httpsRequest($url, $data);

        $result = json_decode($res, true);
        $ticket = $result['ticket'];
        $img = file_get_contents('https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=' . $ticket);

        $dir = './uploads/userQr/';
        if (!is_dir($dir)) mkdir($dir);

        $path = $dir . 'userQr_' . $user_id . '.jpg';

        $data = [
            'path' => substr($path, 1),
            'create_time' => time()
        ];
        $id = Db::name('picture')->insertGetId($data);

        if ($id) {
            file_put_contents($path, $img);
            Db::name('user')->where(['id' => $user_id])->setField('qr', $id);
        }
    }

    return $id ? : '';
}

/**
 * 隐藏手机号
 * @param string $tel 手机号
 * @return string
 * */
function hideMobile($tel)
{
    return $tel ? substr_replace($tel, '****', 3, 4) : '';
}

/**
 * 过滤文本中的emoji表情包（输出到excel文件中会导致问题）
 * @param string $text 原文本
 * @return string 过滤emoji表情包后的文本
 */
function removeEmoji($text)
{
    $len = mb_strlen($text);
    $newText = '';
    for ($i = 0; $i < $len; $i++) {
        $str = mb_substr($text, $i, 1, 'utf-8');
        if (strlen($str) >= 4) continue;//emoji表情为4个字节
        $newText .= $str;
    }

    return $newText;
}

/**
 * workerman
 * 获取后台用户列表用户最新的消息时间
 * @param int $user_id 用户id
 * @return string
 * */
function getUserNewMsgTime($user_id)
{
    $create_time = Db::name('chat')
        ->where(['from_uid' => $user_id])
        ->whereOr(
            function ($query) use ($user_id) {
                $query->where(['to_uid' => $user_id]);
            }
        )
        ->order('create_time desc')
        ->value('create_time');

    return $create_time ? date('H:i:s', $create_time) : '';
}

/**
 * 获取用户的真实 IP
 * */
function getRealIp()
{
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        $ip = $_SERVER['HTTP_CLIENT_IP'];
    } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
    } else {
        $ip = $_SERVER['REMOTE_ADDR'];
    }
    return $ip;
}

/**
 * 目录清单，列出所有文件和文件夹
 * @param string $dir 路径
 * */
function list_files($dir)
{
    if (is_dir($dir)) {
        if ($handle = opendir($dir)) {
            while (($file = readdir($handle)) !== false) {
                if ($file != "." && $file != ".." && $file != "Thumbs.db"/*pesky windows, images..*/) {
                    echo '<a target="_blank" href="' . $dir . $file . '">' . $file . '</a>' . "\n";
                }
            }
            closedir($handle);
        }
    }
}

/**
 * 删除文件夹内容
 * @param string $path 路径
 * */
function Delete($path)
{
    if (is_dir($path) === true) {
        $files = array_diff(scandir($path), ['.', '..']);
        foreach ($files as $file) {
            Delete(realpath($path) . '/' . $file);
        }
        return rmdir($path);
    } else if (is_file($path) === true) {
        return unlink($path);
    }
    return false;
}

/**
 * 计算两个地图坐标之间的距离
 * */
function getDistanceBetweenPointsNew($latitude1, $longitude1, $latitude2, $longitude2)
{
    $theta = $longitude1 - $longitude2;
    $miles = (sin(deg2rad($latitude1)) * sin(deg2rad($latitude2))) + (cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * cos(deg2rad($theta)));
    $miles = acos($miles);
    $miles = rad2deg($miles);
    $miles = $miles * 60 * 1.1515;
    $feet = $miles * 5280;
    $yards = $feet / 3;
    $kilometers = $miles * 1.609344;
    $meters = $kilometers * 1000;
    return compact('miles', 'feet', 'yards', 'kilometers', 'meters');
}

/**
 * 转换字节
 * @param int $bytes 传入字节数值
 * @param int $decimals
 * @return string BKMGTP
 */
function human_filesize($bytes, $decimals = 2)
{
    $sz = 'BKMGTP';
    $factor = floor((strlen($bytes) - 1) / 3);
    return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
}

/**
 * 删除该目录以及该目录下面的所有文件和文件夹
 * @param string $dir 目录
 * @return bool
 */
function removeDir($dirName)
{
    //判断传入参数是否目录，如不是执行删除文件
    if (!is_dir($dirName)) {
        //删除文件
        @unlink($dirName);
    }
    //如果传入是目录，使用@opendir将该目录打开，将返回的句柄赋值给$handle
    $handle = @opendir($dirName);
    //这里明确地测试返回值是否全等于（值和类型都相同）FALSE
    //否则任何目录项的名称求值为 FALSE 的都会导致循环停止（例如一个目录名为“0”）
    while (($file = @readdir($handle)) !== false) {
        //在文件结构中，都会包含形如“.”和“..”的向上结构
        //但是它们不是文件或者文件夹
        if ($file != '.' && $file != '..') {
            //当前文件$dir为文件目录+文件
            $dir = $dirName . '/' . $file;
            //判断$dir是否为目录，如果是目录则递归调用reMoveDir($dirName)函数
            //将其中的文件和目录都删除；如果不是目录，则删除该文件
            is_dir($dir) ? removeDir($dir) : @unlink($dir);
        }
    }
    closedir($handle);
    return rmdir($dirName);
}

/**
 * @param $url
 * @param $data
 * @return mixed|false
 */
function getAiCurl($url, $data = null, $header = null)
{
    $multiCurl = curl_multi_init();
    $curlArray = [];
    $responses = [];

    // 初始化请求
    $curlArray[0] = curl_init();
    curl_setopt($curlArray[0], CURLOPT_URL, $url);
    curl_setopt($curlArray[0], CURLOPT_CONNECTTIMEOUT, 1000); // 连接超时
    curl_setopt($curlArray[0], CURLOPT_TIMEOUT, 1000);        // 请求超时
    curl_setopt($curlArray[0], CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curlArray[0], CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($curlArray[0], CURLOPT_FOLLOWLOCATION, true);

    if (!empty($data)) {
        curl_setopt($curlArray[0], CURLOPT_POST, 1);
        curl_setopt($curlArray[0], CURLOPT_POSTFIELDS, $data);
    }

    if (!empty($header)) {
        $randomIp = rand(1, 255) . '.' . rand(1, 255) . '.' . rand(1, 255) . '.' . rand(1, 255);
        $header[] = 'X-Forwarded-For: ' . $randomIp;
        $header[] = 'Client-IP: ' . $randomIp;
        curl_setopt($curlArray[0], CURLOPT_HTTPHEADER, $header);
    }

    // 设置写入函数以获取响应内容
    curl_setopt($curlArray[0], CURLOPT_WRITEFUNCTION, function ($curl, $data) use (&$responses) {
        $responses[0] .= $data;
        return strlen($data); // 返回写入的数据长度
    });

    // 添加句柄到 multi curl
    curl_multi_add_handle($multiCurl, $curlArray[0]);

    // 执行请求
    $active = null;
    do {
        $mrc = curl_multi_exec($multiCurl, $active);
    } while ($mrc == CURLM_CALL_MULTI_PERFORM);

    while ($active && $mrc == CURLM_OK) {
        if (curl_multi_select($multiCurl) != -1) {
            do {
                $mrc = curl_multi_exec($multiCurl, $active);
            } while ($mrc == CURLM_CALL_MULTI_PERFORM);
        }
    }

    // 移除和关闭句柄
    curl_multi_remove_handle($multiCurl, $curlArray[0]);
    $errorCode = curl_errno($curlArray[0]);    // 获取错误代码
    $errorMessage = curl_error($curlArray[0]); // 获取错误信息
    curl_close($curlArray[0]);
    curl_multi_close($multiCurl);

    // 检查是否有错误
    if ($errorCode) {
        // 处理超时错误
        if ($errorCode === CURLE_OPERATION_TIMEOUTED) {
            file_put_contents(RUNTIME_PATH . '请求超时.txt', $errorCode . '：' . $errorMessage . date('Y-m-d H:i:s') . PHP_EOL, FILE_APPEND);
            return '请求超时: ' . $errorMessage;
        } else {
            file_put_contents(RUNTIME_PATH . '请求错误.txt', $errorCode . '：' . $errorMessage . date('Y-m-d H:i:s') . PHP_EOL, FILE_APPEND);
            return '请求错误: ' . $errorMessage;
        }
    }

    // 返回响应数据
    return isset($responses[0]) ? $responses[0] : false;

}


/**
 * @param $url
 * @param $data
 * @return mixed|false
 */
function getPromptCurl($url, $data = null, $header = null)
{
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    // 设置连接超时时间（单位：秒）
    curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 1000); // 60 秒
    // 设置请求超时时间（单位：秒）
    curl_setopt($curl, CURLOPT_TIMEOUT, 1000);        // 120 秒
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
    if (!empty($data)) {
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
    }
    // 设置header头
    if (!empty($header)) curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

    $response = curl_exec($curl);
    if ($response === false) {
        // 处理 CURL 请求失败的情况
        echo 'CURL Error: ' . curl_error($curl);
        return false;
    } else {
        // 请求成功，返回 API 响应数据
        return $response;
    }

    curl_close($curl);
}

/**
 * 文本智能问答
 * */
function textGeneration($messages)
{
    $url = 'https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation';

    $header = [
        'Content-Type: application/json',
        'Authorization: Bearer ' . C('AI_KEY'),
    ];

    $input = [
        'messages' => $messages
    ];

    /*$parameters = [
    ];*/

    $data = [
        'model' => 'qwen2-72b-instruct',
        'input' => $input,
        //        'parameters' => $parameters
    ];

    $res = getPromptCurl($url, json_encode($data), $header);
    if ($res !== false) {
        // 处理 API 响应数据
        $responseData = json_decode($res, true);

        return $responseData;
    } else {
        return false;
    }
}

/**
 * 选择题答案 概率|随机 选择
 * @param array $questions         问卷问题数组
 * @param int   $numberOfResponses 需要生成的回答数量
 * @param array $probabilities     每个问题的概率数组
 * */
function generateAnswers($questions, $numberOfResponses, $probabilities = [])
{
    $allResponses = [];

    for ($i = 0; $i < $numberOfResponses; $i++) {
        $responses = [];
        $choose_arr = [];
        foreach ($questions as $index => $question) {
            $answers = $question['answers'];// ["A 公路自行车","B 山地自行车","C 电动自行车","D 其他"]

            // 排除问答题
            if ($question['type'] == 3) {
                continue;
            }

            if ($question['type'] == 2) {
                // 多选题处理
                $selectedAnswers = [];
                $questionProbabilities = isset($probabilities[$index]) ? $probabilities[$index] : [];

                if (!empty($questionProbabilities)) {
                    // 控制每个选项的选择次数
                    foreach ($questionProbabilities as $answerIndex => $probability) {
                        $selectedCount = intval($numberOfResponses * ($probability / 100));
                        for ($j = 0; $j < $selectedCount; $j++) {
                            $selectedAnswers[] = $answerIndex + 1; // A=1, B=2, ...
                        }
                    }

                    // 去重并随机打乱
                    $selectedAnswers = array_unique($selectedAnswers);

                    // 如果没有选中任何选项，随机选择至少一个
                    if (empty($selectedAnswers)) {
                        $selectedAnswers[] = rand(1, count($answers)); // 确保至少选择一个选项
                    } else {
                        shuffle($selectedAnswers);
                    }
                } else {
                    // 如果没有设置概率，随机选择至少一个答案
                    $selectedCount = rand(1, count($answers)); // 随机选择数量
                    $selectedAnswers = array_rand(array_flip(range(1, count($answers))), $selectedCount);
                    $selectedAnswers = is_array($selectedAnswers) ? array_values($selectedAnswers) : [$selectedAnswers]; // 确保返回数组
                }

                $responses[] = $selectedAnswers; // 确保多选题答案是一维数组且唯一
            } else {
                // 单选题和下拉框
                $questionProbabilities = isset($probabilities[$index]) ? $probabilities[$index] : [];// [80, 20, 0, 0]
                if (!empty($questionProbabilities)) {
                    $totalProbability = array_sum($questionProbabilities);// 概率之和，100
                    if ($totalProbability > 0) {
                        // 控制数量 START
                        $choose_arr[$index] = getChooseCountArr($questionProbabilities, $numberOfResponses);
                        // 控制数量 END

                        $randomValue = mt_rand(1, $totalProbability);// 随机1-100
                        $cumulativeProbability = 0;
                        for ($answerIndex = 0; $answerIndex < count($answers); $answerIndex++) {
                            $cumulativeProbability += $questionProbabilities[$answerIndex];
                            if ($randomValue <= $cumulativeProbability) {
                                $responses[] = $answerIndex + 1; // 使用 A=1, B=2, ...
                                break;
                            }
                        }
                    } else {
                        // 概率总和为0，随机选择
                        $responses[] = rand(1, count($answers)); // 随机选择一个答案
                    }
                } else {
                    // 随机选择
                    $responses[] = rand(1, count($answers)); // 随机选择一个答案
                }
            }
        }
        $allResponses[] = $responses;
    }

    $allResponses = adjustResponses($allResponses, $choose_arr);

    return $allResponses;
}

function extractIndexFromArray($data, $indexToExtract)
{
    return array_map(function ($item) use ($indexToExtract) {
        return $item[$indexToExtract];
    }, $data);
}

function getChooseCountArr($questionProbabilities, $numberOfResponses)
{
    $choose_count_arr = [];                                            // 预定好各个选项被选中的次数
    foreach ($questionProbabilities as $choose_index => $probability) {
        // 向上取整
        $rand = rand(0, 1);
        $count = $rand ? ceil($numberOfResponses * $probability / 100) : floor($numberOfResponses * $probability / 100);
        $choose_count_arr[$choose_index] = $count;
    }
    $allCount = array_sum($choose_count_arr);


    if ($allCount > $numberOfResponses) {
        $do_choose_count_arr = array_filter($choose_count_arr, function ($value) {
            return $value > 0; // 只保留大于0的值
        });

        $min = min($do_choose_count_arr);
        $minIndex = array_search($min, $choose_count_arr);
        $choose_count_arr[$minIndex] -= ($allCount - $numberOfResponses);
    }
    if ($allCount < $numberOfResponses) {
        $maxIndex = array_search(max($choose_count_arr), $choose_count_arr);
        $choose_count_arr[$maxIndex] += ($numberOfResponses - $allCount);
    }

    return $choose_count_arr;
}

function adjustResponses($allResponses, $choose_arr) {
    // 初始化选项计数
    $currentCounts = [];

    // 统计当前选中次数
    foreach ($allResponses as $response) {
        foreach ($choose_arr as $questionIndex => $selectionCounts) {
            $selectedOption = $response[$questionIndex];
            if (!isset($currentCounts[$questionIndex])) {
                $currentCounts[$questionIndex] = array_fill(0, count($selectionCounts), 0);
            }
            $currentCounts[$questionIndex][$selectedOption - 1]++;
        }
    }

    // 调整每个问卷的答案
    foreach ($allResponses as &$response) {
        foreach ($choose_arr as $questionIndex => $selectionCounts) {
            $neededCounts = $selectionCounts;

            // 找到当前选项
            $currentOption = $response[$questionIndex];
            $currentCount = $currentCounts[$questionIndex][$currentOption - 1];

            // 增加选项
            if ($currentCount < $neededCounts[$currentOption - 1]) {
                for ($optionIndex = 0; $optionIndex < count($neededCounts); $optionIndex++) {
                    if ($currentCounts[$questionIndex][$optionIndex] < $neededCounts[$optionIndex]) {
                        $response[$questionIndex] = $optionIndex + 1;
                        $currentCounts[$questionIndex][$currentOption - 1]--;
                        $currentCounts[$questionIndex][$optionIndex]++;
                        break;
                    }
                }
            }
            // 减少选项
            elseif ($currentCount > $neededCounts[$currentOption - 1]) {
                for ($optionIndex = 0; $optionIndex < count($neededCounts); $optionIndex++) {
                    if ($optionIndex !== $currentOption - 1 && $currentCounts[$questionIndex][$optionIndex] < $neededCounts[$optionIndex]) {
                        $response[$questionIndex] = $optionIndex + 1;
                        $currentCounts[$questionIndex][$currentOption - 1]--;
                        $currentCounts[$questionIndex][$optionIndex]++;
                        break;
                    }
                }
            }
        }
    }

    return $allResponses;
}


/**
 * 提取问卷调查中的问答题题目
 * @param array $questions 问卷调查中的所有题目
 * */
function extractOpenEndedQuestions($questions)
{
    $openEndedQuestions = [];

    foreach ($questions as $questionData) {
        if (empty($questionData['answers'])) {
            // 去除题号部分，并将问题添加到结果数组中
            $questionText = preg_replace('/^\*\d+\.\s*/', '', $questionData['question']);
            $openEndedQuestions[] = $questionText;
        }
    }

    return $openEndedQuestions;
}

function removeEmptyArraysAfterLastNonEmpty($data)
{
    // 找到最后一个非空数组的位置
    $lastNonEmptyIndex = 0;
    foreach ($data as $index => $array) {
        if (!empty($array)) {
            $lastNonEmptyIndex = $index;
        }
    }

    // 使用 array_slice 保留从 0 到最后一个非空数组的部分
    return array_slice($data, 0, $lastNonEmptyIndex + 1);
}

function checkChromeDriverStatus()
{
    $ch = curl_init('http://localhost:9515/status');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    curl_close($ch);

    // 检查响应
    if ($response) {
        $data = json_decode($response, true);
        // 检查是否包含 "ready" 字段且为 true
        if (isset($data['value']['ready']) && $data['value']['ready'] === true) {
            return true;
        }
    }
    return false;
}

function startChromeDriver()
{
    $command = '"C:\Program Files\Google\Chrome\Application\chromedriver.exe" --port=9515';
    $output = [];
    $return_var = 0;
    exec($command, $output, $return_var);

    if ($return_var === 0) {
        // 等待一段时间以确保 ChromeDriver 启动
        sleep(2); // 可以根据需要调整等待时间

        // 检查 ChromeDriver 是否在运行
        $processCheck = [];
        exec('tasklist | findstr chromedriver.exe', $processCheck);

        // 检查 ChromeDriver 是否运行
        if (count($processCheck) > 0) {
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

function checkSeleniumWebDriverStatus()
{
//    $url = "http://172.20.35.62:4444/wd/hub/status";
    $url = "http://127.0.0.1:4444/wd/hub/status";

    // 初始化 cURL
    $ch = curl_init($url);

    // 设置 cURL 选项
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回内容而不是输出
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);          // 设置超时

    // 执行 cURL 请求
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); // 获取 HTTP 状态码

    // 关闭 cURL
    curl_close($ch);

    // 判断服务是否正常
    if ($httpCode == 200) {
        // 解析返回的 JSON 数据
        $data = json_decode($response, true);
        if (isset($data['value']) && $data['value']['ready'] === true) {
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

function startSeleniumWebDriver()
{
    $command = 'taskkill /F /IM java.exe';
    $output = [];
    $return_var = 0;
    exec($command, $output, $return_var);

    sleep(1); // 等待一段时间以确保 Java 进程被终止

    $command = 'java -jar C:\selenium-server-4.24.0.jar standalone';
    $output = [];
    $return_var = 0;
    exec($command, $output, $return_var);

    if ($return_var === 0) {
        // 等待一段时间以确保 SeleniumWebDriver 启动
        sleep(2); // 可以根据需要调整等待时间

        // 检查 SeleniumWebDriver 是否在运行
        $processCheck = [];
        exec('tasklist | findstr java.exe', $processCheck);

        // 检查 SeleniumWebDriver 是否运行
        if (count($processCheck) > 0) {
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

/**
 * 处理数组，按照指定规则进行删除或更新
 * @param array $data 要处理的数组
 * @return array 处理后的数组
 */
function processArray($data)
{
    foreach ($data as $key => &$item) {
        // 检查是否存在 answer_count 和 complete_count
        if (isset($item['answer_count']) && isset($item['complete_count'])) {
            // 条件1：answer_count 小于等于 complete_count 时，删除这一项
            if ($item['answer_count'] <= $item['complete_count']) {
                unset($data[$key]);
            } elseif ($item['answer_count'] > $item['complete_count']) {
                $textArray = json_decode($item['text'], true);
                // 条件2：answer_count 大于 complete_count 时，删除 text 中的前 complete_count 项

                // 确保 'text' 存在，并且是字符串
                if (is_array($textArray)) {
                    // 删除前 complete_count 项
                    $item['text'] = array_slice($textArray, $item['complete_count']);
                }
            }
        }
    }
    return array_values($data); // 重置索引
}

// 启动ps1
function startPs1()
{
    // 锁定文件路径
    $lockFile = RUNTIME_PATH . 'powershell_lock.lock';

    // 检查锁定文件是否存在
    if (file_exists($lockFile)) {
        file_put_contents(RUNTIME_PATH . 'powershell_status.txt', '进程已经在运行中' . PHP_EOL, FILE_APPEND);
        return; // 如果锁定文件存在，退出函数
    }

    // 创建锁定文件，防止多次启动
    file_put_contents($lockFile, 'locked');

    // PowerShell脚本的完整路径
//    $ps1FilePath = "C:\\phpstudy_pro\\WWW\\auto_answer\\ai.ps1";
    $ps1FilePath = "D:\\www\\auto_answer\\ai.ps1";

    // 组装命令：使用start命令在后台启动 PowerShell 脚本
    $command = "start /B powershell -ExecutionPolicy Bypass -File \"$ps1FilePath\" -WindowStyle Hidden";

    // 使用pclose和popen来执行命令并确保不阻塞PHP进程
    pclose(popen($command, 'r'));

    file_put_contents(RUNTIME_PATH . 'powershell_status.txt', '脚本已启动' . PHP_EOL, FILE_APPEND);
}


// 停止ps1
function stopPs1()
{
    // 查找所有正在运行的 PowerShell 进程
    $command = "tasklist /FI \"IMAGENAME eq powershell.exe\"";
    $runningProcesses = shell_exec($command);

    // 如果找到相关的 PowerShell 进程
    if (!empty($runningProcesses)) {
        // 使用 taskkill 来终止所有 PowerShell 进程
        $stopCommand = "taskkill /F /IM powershell.exe";
        shell_exec($stopCommand);

        file_put_contents(RUNTIME_PATH . 'powershell_status.txt', '所有PowerShell进程已终止' . PHP_EOL, FILE_APPEND);

        // 删除锁定文件，表示进程已停止
        $lockFile = RUNTIME_PATH . 'powershell_lock.lock';
        if (file_exists($lockFile)) {
            unlink($lockFile);
            file_put_contents(RUNTIME_PATH . 'powershell_status.txt', '锁定文件已删除' . PHP_EOL, FILE_APPEND);
        }
    } else {
        file_put_contents(RUNTIME_PATH . 'powershell_status.txt', '未找到运行中的PowerShell进程' . PHP_EOL, FILE_APPEND);
    }
}

// 通用服务重启函数
function restartService($stopCommand, $startCommand, $processName, $startTimeout = 10)
{
    $result = [];

    // 终止进程
    exec($stopCommand, $output, $return_var);
    $result['stop'] = ['output' => $output, 'status' => $return_var];

    // 使用start命令在后台启动进程，防止阻塞PHP进程
    pclose(popen('start /B cmd /c "' . $startCommand . '"', 'r'));

    // 轮询检查进程是否启动成功
    $startTime = time();
    while (!checkProcessRunning($processName) && (time() - $startTime) < $startTimeout) {
        usleep(500000); // 每0.5秒检查一次，最多等待设定的秒数
    }

    if (checkProcessRunning($processName)) {
        $result['status'] = 'success';
    } else {
        $result['status'] = 'failed';
    }

    return $result;
}

// 重启所有服务
function restartAllServers($plan_id)
{
    $results = [];

    // 重启 chromedriver 服务
    $stopChromedriver = "taskkill /F /IM chromedriver.exe";
    $startChromedriver = '"C:\Program Files\Google\Chrome\Application\chromedriver.exe" --port=9515';
    $results['chromedriver'] = restartService($stopChromedriver, $startChromedriver, 'chromedriver.exe');

    // 重启 selenium-server 服务
    $stopSeleniumServer = "taskkill /F /IM java.exe";
    $startSeleniumServer = 'java -jar C:\selenium-server-4.24.0.jar standalone';
    $results['seleniumServer'] = restartService($stopSeleniumServer, $startSeleniumServer, 'java.exe');

    // 启动 Chrome 并打开指定 URL
//    $command = 'start chrome.exe "http://106.14.75.184/index/Ai/auto/plan_id/' . $plan_id . '"';
    $command = 'start chrome.exe "http://www.autoAnswer.com/index/Ai/auto/plan_id/' . $plan_id . '"';
    pclose(popen('start /B cmd /c "' . $command . '"', 'r'));

    return $results;
}

// 辅助函数：检查进程是否正在运行
function checkProcessRunning($processName)
{
    $tasklist = shell_exec("tasklist");
    return strpos($tasklist, $processName) !== false;
}


/**
 * @param string $string        要处理的字符串
 * @param int    $questionCount 题目数量
 * @param int    $answerCount   问卷数量
 * */
function assembleSurveyData($string, $questionCount, $answerCount)
{
    // 用正则表达式提取被双引号包裹的句子
    preg_match_all('/"([^"]*)"/', $string, $matches);

    $results = $matches[1]; // 提取的句子

    // 验证数据是否符合要求
    if (count($results) < $questionCount * $answerCount) {
        return false; // 如果句子总数不够
    }

    // 构建最终的数据结构
    $finalData = array_fill(0, $answerCount, array_fill(0, $questionCount, ''));

    // 将句子填入最终结构
    for ($i = 0; $i < $answerCount; $i++) {
        for ($j = 0; $j < $questionCount; $j++) {
            $index = $i * $questionCount + $j;
            if ($index < count($results)) {
                $finalData[$i][$j] = $results[$index];
            }
        }
    }

    return $finalData;
}

function countTypeThreeQuestions($data)
{

    if ($data === null) {
        return false; // 如果解析失败，返回 false
    }

    // 统计 type 为 3 的问题数量
    $count = 0;
    foreach ($data as $item) {
        if (isset($item['type']) && $item['type'] === 3) {
            $count++;
        }
    }

    return $count;
}

function validateSurveyData($data, $expectedQuestions, $expectedAnswers)
{
    // 首先检查数据是否是一个有效的数组
    if (!is_array($data) || count($data) !== $expectedAnswers) {
        return false; // 返回 false 如果数据不是数组或答案数量不匹配
    }

    // 检查每份答案的题目数量是否符合预期
    foreach ($data as $answers) {
        if (!is_array($answers) || count($answers) !== $expectedQuestions) {
            return false; // 返回 false 如果某份答案的题目数量不匹配
        }
    }

    return true; // 如果所有校验通过，返回 true
}

function getStatusDesc($status)
{
    //答题程序状态；0：自动答题程序未启动，1：自动答题程序已启动，等待答题，2：自动答题程序运行中，3：自动答题程序已停止
    switch ($status) {
        case 0:
            $status_desc = '未启动';
            break;
        case 1:
            $status_desc = '已启动，等待答题';
            break;
        case 2:
            $status_desc = '运行中';
            break;
        case 3:
            $status_desc = '已停止';
            break;
        default:
            $status_desc = '未知状态';
            break;
    }
    return $status_desc;
}